<?php
/**
 * 模块管理器
 * 
 * 可以用该对象安装或卸载某个“安装型”的模块。安装型的模块可以在自己的模块下面有一个独立的MVC环境，
 * 并可以有安装程序，它的目录结构大致为：
 * <pre>
 * $IF_APP_ROOT/lib/modules/module_name
 * 								/_assets  	=> 静态资源，比如图片、脚本等等，模块安装后会被部署到应用目录的 assets/ 目录下
 * 								/_deploy 	=> 需要部署在当前系统中的资源（I-F并不会部署这些资源到其他地方，但可以被分配一个新的MVC环境）
 * 									/controllers
 * 										/_install.php 	=> 安装程序
 * 									/helpers
 * 									/filters
 * 									/models
 * 									/views	
 * </pre>
 *
 * @package if
 * @subpackage installer
 * @since 1.1.2
 */
class IModuleManager extends IObject {
	private static $instance;
	private $registry = array();
	private $registryFile;
	
	/**
	 * Enter description here...
	 *
	 * @var IApplication
	 */
	private $app;
	
	/**
	 * Enter description here...
	 *
	 * @since 1.1.2
	 */
	private function __construct() {
		$this->app = IApplication::singleton();
		$this->registryFile = $this->app->configDir() . "/_registry.php";
		$this->registry = o("@registry");// id => array( version, name, status )
		if (!is_array($this->registry)) {
			$this->registry = array();
		}
	}
	
	/**
	 * 取得管理器的一个单例
	 *
	 * @return IModuleManager
	 * @since 1.1.2
	 */
	static function singleton() {
		if (!self::$instance) {
			self::$instance = new self;
		}
		return self::$instance;
	}
	
	/**
	 * 根据模块名取得模块对象
	 *
	 * @param string $name 模块名（和模块目录对应的名字，而不是在模块目录下的_module.php配置的名字）
	 * @return IModule
	 * @since 1.1.2
	 */
	function moduleByName($name) {
		$modules = $this->app->modules();
		if (isset($modules[$name])) {
			$moduleConfigFile = $modules[$name] . "/_module.php";
			if (is_file($moduleConfigFile)) {
				$module = new IModule($moduleConfigFile);
				$module->setDir($modules[$name]);
				return $module;
			}
			else {
				$module = new IModule(array());
				$module->setDir($modules[$name]);
				$module->setName($name);
				return $module;
			}
		}
		return null;
	}
	
	/**
	 * 根据模块ID取得模块对象
	 *
	 * @param string $id 模块ID，该ID可以在模块目录下的_module.php中配置
	 * @return IModule
	 * @since 1.1.2
	 */
	function moduleById($id) {
		foreach ($this->app->modules() as $name => $dir) {
			$moduleConfigFile = $dir . "/_module.php";
			if (is_file($moduleConfigFile)) {
				$module = new IModule($moduleConfigFile);
				if ($module->id() == $id) {
					$module->setDir($dir);
					return $module;
				}
			}
		}
		return null;
	}
	
	/**
	 * 检查有无新的需要安装的模块
	 * 
	 * 如果有新的需要安装的模块，则会自动调用模块下的安装程序
	 * 
	 * @since 1.1.2
	 */
	function detect() {
		foreach ($this->app->modules() as $name => $dir) {
			$moduleConfigFile = $dir . "/_module.php";
			if (is_file($moduleConfigFile)) {
				$module = new IModule($moduleConfigFile);
				$module->setDir($dir);
				if ($module->isInstallable()) {
					$id = $module->id();
					if (!isset($this->registry[$id])) {
						//调用安装流程
						$this->_startInstallProcess($module);
						break;
					}
					else {
						$version = $this->registry[$id]["version"];
						$status = $this->registry[$id]["version"];
						if (version_compare($version, $module->version()) > 0) {//新版本小
							if ($status != IModule::STATUS_INSTALLED) {//老版本没有安装
								//调用安装流程
								$this->_startInstallprocess($module);
								break;
							}
						}
						else if (version_compare($version, $module->version()) == 0) {//版本一样
							//do nothing
						}
						else {//新版本大
							//调用安装流程
							$this->_startInstallProcess($module);
							break;
						}
					}
				}
			}
		}
	}
	
	private function _startInstallProcess(IModule $module) {
		if_shut_compiler();
		
		//是否有安装程序
		$installFile = $module->dir() . "/_deploy/controllers/_install.php";
		if (is_file($installFile)) {
			require($installFile);
			
			if (!class_exists("_installController", false)) {
				exit("you must define _installController class in install file '{$installFile}'");
			}
			if (!is_subclass_of("_installController", "IModuleController")) {
				exit("class _installController must be inherited from IModuleController");
			}
			
			$router = new IRouter();
			$array = $router->parsePath(__PATH__);
			
			$controller = new _installController();
			$controller->setRouter($router);
			$controller->setModule($module);
			$controller->setAction($array["action"]);
			$controller->exec();
		}
		else {
			echo ("module " . $module->name() . " v" . $module->version() . " is being installed to your application") . "<br/>\n";
			$this->install($module);
			echo ("module " . $module->name() . " v" . $module->version() . " is installed successfully") . "<br/>\n";
			echo "Please refresh the page<br/>\n";
		}
		exit;
	}
	
	/**
	 * 注册某个模块
	 * 
	 * 一旦注册了该模块，就标志着模块的安装结束
	 *
	 * @param IModule $module 模块对象
	 * @since 1.1.2
	 */
	function register(IModule $module) {
		$this->registry[$module->id()] = array(
			"version" => $module->version(),
			"name" => $module->name(),
			"status" => IModule::STATUS_INSTALLED
		);
		$this->_save();
	}
	
	/**
	 * 更改模块的状态为非注册状态
	 *
	 * @param IModule $module 模块对象
	 * @since 1.1.2
	 */	
	function unRegister(IModule $module) {
		$this->registry[$module->id()] = array(
			"version" => $module->version(),
			"name" => $module->name(),
			"status" => IModule::STATUS_UNINSTALLED
		);
		$this->_save();
	}
	
	/**
	 * 跳过某个模块不安装
	 *
	 * @param IModule $module 模块对象
	 * @since 1.1.2
	 */
	function skip(IModule $module) {
		$this->registry[$module->id()] = array(
			"version" => $module->version(),
			"name" => $module->name(),
			"status" => IModule::STATUS_SKIPPED
		);
		$this->_save();
	}
	
	private function _save() {
		$config = new IConfig($this->registryFile);
		$config->setOpts($this->registry);
		$config->write();
	}
	
	/**
	 * 备份文件到workspace/module下
	 *
	 * @since 1.1.2
	 */
	function backup($source, $to) {
		$file = new IFile($source);
		$file->copyTo(IF_MODULE_WORKPLACE . $to);
	}
}

?>